﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Text;

namespace iBet.Pages
{
    public partial class TicketCalculator : System.Web.UI.Page
    {
        #region Properties
        public string Token
        {
            get
            {
                if (ViewState["token"] != null)
                    return ViewState["token"].ToString();

                return string.Empty;
            }
            set
            {
                ViewState.Add("token", value);
            }

        }

        public List<TicketItem> TicketItems
        {
            get
            {
                if (Cache[this.Token] != null)
                    return (List<TicketItem>)Cache[this.Token];

                return null;
            }
            set
            {
                Cache.Insert(this.Token, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(15));
            }
        }

        public int NumberOfSystems { get; set; }

        /// <summary>
        /// Flag that defines if method on the min system ticket needs to be raised or not
        /// </summary>
        public bool RaiseSystemMinEvent
        {
            get
            {
                if (ViewState["RaiseSystemMinEvent"] != null)
                    return (bool)ViewState["RaiseSystemMinEvent"];
                return false;
            }
            set
            {
                ViewState.Add("RaiseSystemMinEvent", value);
            }
        }
        #endregion

        #region Form Handlers
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                this.Token = Guid.NewGuid().ToString();
                btnAddItem_Click(btnAddItem, null);
            }
        }

        protected void btnCalculate_Click(object sender, EventArgs e)
        {
            CalculateProfit();
        }

        protected void txtSystemType_TextChanged(object sender, EventArgs e)
        {
            ListViewDataItem ticketDataItem = (sender as TextBox).Parent as ListViewDataItem;

            if (ticketDataItem == null)
                return;

            TextBox txtSystemTypeMin = ticketDataItem.FindControl("txtSystemTypeMin") as TextBox;
            TextBox txtSystemTypeMax = ticketDataItem.FindControl("txtSystemTypeMax") as TextBox;

            if (txtSystemTypeMin == null || txtSystemTypeMax == null)
                return;

            //the minimum is initialized to zero, because the system needs to be shown even if only the system max is defined
            int min, max;
            if (int.TryParse(txtSystemTypeMax.Text, out max))
            {
                if (!int.TryParse(txtSystemTypeMin.Text, out min))
                    min = 0;

                if (min > max)
                {
                    txtSystemTypeMin.Text = txtSystemTypeMax.Text;
                    //return;
                }

                RebuildTicketItems();
                List<Odd> odds = this.TicketItems[ticketDataItem.DataItemIndex].Odds;

                if (odds == null || odds.Count == 0)
                {
                    odds = new List<Odd>();

                    for (int i = 0; i < max; i++)
                    {
                        Odd odd = new Odd();
                        odd.IsHit = true;

                        odds.Add(odd);
                    }
                }
                else
                {
                    //There was a change of the max of the system
                    if (odds.Count != max)
                    {
                        int numberOfIterations = System.Math.Abs((max - odds.Count));
                        for (int i = 0; i < numberOfIterations; i++)
                        {
                            if (max > odds.Count)
                            {
                                Odd newOdd = new Odd();
                                newOdd.IsHit = true;
                                odds.Add(newOdd);
                            }
                            else
                                //The max has been decreased, so delete the last item
                                odds.RemoveAt(odds.Count - 1);
                        }
                    }
                }

                this.TicketItems[ticketDataItem.DataItemIndex].Odds = odds;
                ListView lvOdds = ticketDataItem.FindControl("lvOdds") as ListView;

                if (lvOdds == null)
                    return;

                BindOdds(lvOdds, ticketDataItem.DataItemIndex);
                TicketItem ticketItem = this.TicketItems[ticketDataItem.DataItemIndex];

                if (ticketItem.SystemType == null)
                    ticketItem.SystemType = new SystemType() { Min = min, Max = max };
                else
                {
                    ticketItem.SystemType.Min = min;
                    ticketItem.SystemType.Max = max;
                }
            }
        }

        protected void lvItems_ItemDeleting(object sender, ListViewDeleteEventArgs e)
        {
            RebuildTicketItems();
            this.TicketItems.RemoveAt(e.ItemIndex);
            ReloadTicketItems();

            CalculateProfit();
        }

        protected void btnNewTicket_Click(object sender, EventArgs e)
        {
            this.TicketItems = new List<TicketItem>();
            btnAddItem_Click(null, null);
        }

        protected void btnAddItem_Click(object sender, EventArgs e)
        {
            TicketItem emptyItem = new TicketItem();

            if (sender != null)
                RebuildTicketItems();

            this.TicketItems.Add(emptyItem);
            ReloadTicketItems();

            CalculateProfit();
        }

        protected void lvItems_ItemDataBound(object sender, ListViewItemEventArgs e)
        {
            if (e.Item.ItemType == ListViewItemType.DataItem)
            {
                ListView lvOdds = e.Item.FindControl("lvOdds") as ListView;
                if (lvOdds != null)
                {
                    ListViewDataItem item = e.Item as ListViewDataItem;
                    BindOdds(lvOdds, item.DataItemIndex);
                }

                TextBox txtSystemTypeMin = e.Item.FindControl("txtSystemTypeMin") as TextBox;
                TextBox txtSystemTypeMax = e.Item.FindControl("txtSystemTypeMax") as TextBox;

                if (txtSystemTypeMin != null)
                {
                    string jsFunction = string.Format("CheckSystemType('{0}','{1}')", txtSystemTypeMin.ClientID, txtSystemTypeMax.ClientID);
                    txtSystemTypeMin.Attributes.Add("onblur", jsFunction);
                }
            }
        }

        protected void lvItems_DataBound(object sender, EventArgs e)
        {
            if (lvItems.Items.Count == 1)
            {
                LinkButton btnDelete = lvItems.Items[0].FindControl("btnDeleteItem") as LinkButton;
                btnDelete.Visible = false;
            }
        }

        protected void lvOddsItemDataBound(object sender, ListViewItemEventArgs e)
        {
            if (e.Item.ItemType == ListViewItemType.DataItem)
            {
                ListViewDataItem oddItem = e.Item as ListViewDataItem;

                if (oddItem.DataItemIndex == 0)
                {
                    TextBox txtOdd = oddItem.FindControl("txtOdd") as TextBox;
                    txtOdd.Focus();
                }
            }
        }
        #endregion

        #region Custom Methods
        private void ReloadTicketItems()
        {
            lvItems.DataSource = TicketItems.Select(p => new
            {
                SystemName = "System " + (TicketItems.IndexOf(p) + 1).ToString(),
                Min = (p.SystemType == null) ? string.Empty : p.SystemType.Min.ToString(),
                Max = (p.SystemType == null) ? string.Empty : p.SystemType.Max.ToString(),
            }).ToList();

            lvItems.DataBind();
        }

        private void BindOdds(ListView lvOdds, int itemIndex)
        {
            TicketItem ticketItem = this.TicketItems[itemIndex];

            if (ticketItem.Odds != null && ticketItem.Odds.Count > 0)
            {
                lvOdds.DataSource = ticketItem.Odds.Select(p => new
                {
                    MatchName = "Match " + (ticketItem.Odds.IndexOf(p) + 1).ToString(),
                    IsHit = p.IsHit,
                    Odd = (p.Value == 0) ? string.Empty : p.Value.ToString(),
                }).ToList();
                lvOdds.DataBind();
            }
        }

        private void RebuildTicketItems()
        {
            //Rebuild the ticket items object
            this.TicketItems = new List<TicketItem>();
            foreach (ListViewDataItem ticketDataItem in lvItems.Items)
            {
                TicketItem ticketItem = new TicketItem();

                TextBox txtSystemTypeMin = ticketDataItem.FindControl("txtSystemTypeMin") as TextBox;
                TextBox txtSystemTypeMax = ticketDataItem.FindControl("txtSystemTypeMax") as TextBox;

                if (txtSystemTypeMin == null || txtSystemTypeMax == null)
                    return;

                int min, max;
                if (int.TryParse(txtSystemTypeMin.Text, out min) && int.TryParse(txtSystemTypeMax.Text, out max))
                {
                    ticketItem.SystemType = new SystemType() { Min = min, Max = max };

                    ListView lvOdds = ticketDataItem.FindControl("lvOdds") as ListView;
                    if (lvOdds == null)
                        return;

                    ticketItem.Odds = new List<Odd>();
                    foreach (ListViewDataItem oddDataItem in lvOdds.Items)
                    {
                        TextBox txtOdd = oddDataItem.FindControl("txtOdd") as TextBox;
                        CheckBox chkIsHit = oddDataItem.FindControl("chkIsHit") as CheckBox;

                        if (txtOdd == null || chkIsHit == null)
                            return;

                        double oddValue = 0;
                        double.TryParse(txtOdd.Text, out oddValue);

                        ticketItem.Odds.Add(new Odd() { Value = oddValue, IsHit = chkIsHit.Checked });
                    }
                }
                this.TicketItems.Add(ticketItem);
            }
        }

        private void ClearProfit()
        {
            txtProfitValue.Text = "0";
        }

        private void CalculateProfit()
        {
            Ticket ticket = new Ticket();

            double bet;
            if (!double.TryParse(txtBetValue.Text, out bet))
                return;
            ticket.Bet = bet;

            //Rebuild the ticket items object
            RebuildTicketItems();

            ticket.Items = this.TicketItems;

            //Get JSon string from WCF Contract
            string jsonTicket = SerializeTicket(ticket);
            Ticket deserializedTicket = DeserializeTicket(jsonTicket);

            Calculator calculator = new Calculator();
            txtProfitValue.Text = calculator.CalculateTicket(ticket).ToString();
        }

        /// <summary>
        /// This method returns serialized JSON string from WCF Data Contract
        /// This can be an extension method for Ticket class
        /// </summary>
        /// <param name="ticket"></param>
        /// <returns></returns>
        public string SerializeTicket(Ticket ticket)
        {
            /// Serialize to JSON
            System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(ticket.GetType());
            MemoryStream ms = new MemoryStream();
            serializer.WriteObject(ms, ticket);
            string json = Encoding.Default.GetString(ms.ToArray());
            return json;
        }

        /// <summary>
        /// This method returns serialized JSON string from WCF Data Contract
        /// This can be an extension method for Ticket class
        /// </summary>
        /// <param name="ticket"></param>
        /// <returns></returns>
        public Ticket DeserializeTicket(string jsonTicket)
        {
            Ticket ticket = new Ticket();
            MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonTicket));
            System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(ticket.GetType());
            ticket = serializer.ReadObject(ms) as Ticket;
            ms.Close();
            return ticket;
        }
        #endregion
    }
}
